package com.xrh.core.utils;


import java.io.InputStream;
import java.util.Date;
import java.util.regex.Pattern;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.xrh.core.dto.UploadFileInfoDTO;
import com.xrh.core.exceptions.BusinessException;
import com.xrh.core.interfaces.FileUtilsInf;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.ObjectWriteResponse;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;

/**
 * @author 肖荣辉
 * @function minio工具类
 * @date 2022年12月14日
 */
//当文件服务器配置为minioFileServer才实例化该 MinioUtils
@ConditionalOnProperty(value = "fileServer.name", havingValue = "minioFileServer")
@Component("minioFileServer")
public class MinioUtils implements FileUtilsInf {

    @Value("${minio.endpoint}")
    private String endpoint;

    @Value("${minio.userName}")
    private String userName;

    @Value("${minio.password}")
    private String password;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Value("${minio.savePath}")
    private String savePath;

    @Value("${minio.downloadHost}")
    private String downloadHost;

    //配置fastdfs的http下载的前缀 如 10.20.60.1:6138
    @Value("${minio.httpDownload.downloadPrePath:}")
    private String downloadPrePath;

    /**
     * @function 检查配置是否为空
     * @author 肖荣辉
     * @date 2022年12月14日
     */
    public void checkConfig() {

        if (StrUtil.isBlank(endpoint))
            throw new BusinessException("minio.endpoint未配置");

        if (StrUtil.isBlank(userName))
            throw new BusinessException("minio.userName未配置");

        if (StrUtil.isBlank(password))
            throw new BusinessException("minio.password未配置");

        if (StrUtil.isBlank(bucketName))
            throw new BusinessException("minio.bucketName未配置");

        if (StrUtil.isBlank(savePath))
            throw new BusinessException("minio.savePath未配置");

        if (StrUtil.isBlank(downloadHost))
            throw new BusinessException("minio.downloadHost未配置");

    }


    /**
     * @function 上传文件
     * @author 肖荣辉
     * @date 2022年12月14日
     */
    public UploadFileInfoDTO upload(MultipartFile file) throws Exception {
        return upload(file.getInputStream(), file.getOriginalFilename(), file.getSize());
    }


    /**
     * @function 上传文件
     * @author 肖荣辉
     * @date 2022年12月14日
     */
    public UploadFileInfoDTO upload(InputStream inputStream, String fileName, long fileSize) throws Exception {

        if (inputStream == null)
            throw new BusinessException("输入流不能为空");

        if (StrUtil.isEmpty(fileName))
            throw new BusinessException("文件名不能为空");

        String contentType = FileUtil.getMimeType(fileName);

        String suffix = FileUtil.getSuffix(fileName);

        //校验配置
        checkConfig();

        //1. 创建一个客户端
        MinioClient client = MinioClient.builder().endpoint(endpoint) //本地ip+端口号
                .credentials(userName, password)  //用户名和密码
                .build();

        //3.判断桶是否存在 , 不存在则创建
        if (!client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()))
            client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());

        //用uuid生成一个新的文件名
        String newFileName = UUID.fastUUID() + (StrUtil.isBlank(suffix) ? "" : "." + suffix);

        contentType = contentType == null ? "application/octet-stream" : contentType;

        String curDateStr = DateUtil.format(new Date(), "yyyy/MM/dd");

        String newSavePath = savePath + "/" + curDateStr;

        ObjectWriteResponse response = client.putObject(PutObjectArgs.builder()
                .bucket(bucketName)   //桶名
                .object(newSavePath + "/" + newFileName) //要生成的对象名
                .stream(inputStream, fileSize, -1)
                .contentType(contentType)
                .build());


        String filePath = downloadHost + "/" + bucketName + "/" + newSavePath + "/" + newFileName;

        return new UploadFileInfoDTO(fileName, response.object(), filePath, fileSize, contentType, suffix);

    }

    /**
     * @param uri
     * @function 下载文件
     * @author 肖荣辉
     * @date 2021年8月25日
     */
    public byte[] downFile(String url) {
    	
        if (StrUtil.isBlank(downloadPrePath)) {
            return HttpUtil.createGet(url).execute().bodyBytes();
        }
        
        //配置了http下载前缀，则替换下，如之前文件路径为 http://10.30.105.6/file/shuyao/gcp/2023/06/29/a3ce9e84-bbd1-43a0-984f-d60c8e85059f.docx
        //配置的为10.20.60.1:6138/file，则需要下载路径为10.20.60.1:6138/file/shuyao/gcp/2023/06/29/a3ce9e84-bbd1-43a0-984f-d60c8e85059f.docx
        String afterReplacePath = ReUtil.replaceFirst(Pattern.compile("(.*?)/file"), url, downloadPrePath + "/file");
        
        return HttpUtil.downloadBytes(afterReplacePath);
    }

    /**
	 * @function 删除文件
	 * @param filePath
	 * @author 肖荣辉
	 * @date 2023年12月21日
	*/
    public void deleteFile(String filePath) throws Exception {
    	
        //1. 创建一个客户端
        MinioClient client = MinioClient.builder().endpoint(endpoint) //本地ip+端口号
                .credentials(userName, password)  //用户名和密码
                .build();

        //文件路径需包含桶名/
        String prefixPath = bucketName + "/";

        XAssert.isTrue(filePath.contains(prefixPath), "文件路径不正确 , 路径需包含" + prefixPath);
        
        //删除的文件路径截取自桶名之后
        String objectName = StrUtil.subAfter(filePath, prefixPath, false);

        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build();
        
        // 删除文件
        client.removeObject(removeObjectArgs);
    }


}

